home *** CD-ROM | disk | FTP | other *** search
- /*
- * Gateway Discovery Protocol daemon suitable for using on Un*x systems.
- *
- * September 1989, Greg Satz
- *
- * Copyright (c) 1989 by cisco Systems, Inc.
- * All rights reserved.
- */
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <sys/file.h>
- #include <sys/mbuf.h>
-
- #ifdef FD_SETSIZE
- #define getdtablesize() FD_SETSIZE
- #endif
-
- #include <net/if.h>
- #include <net/route.h>
- #include <netinet/in.h>
- #include <netinet/if_ether.h>
- #include <netinet/arp.h>
-
- /* required for HP vna_table processing */
- #include <net/probe_gdp.h>
- #include <net/raw8023.h>
- #include <net/lan_802.h>
- #include <netinet/probe_2.h>
- #include <fcntl.h>
- #include <memory.h>
-
- #include <stdio.h>
- #include <signal.h>
- #include <errno.h>
- #include <netdb.h>
- #include <syslog.h>
- #include <nlist.h>
-
- /*
- * Gateway Discovery Protocol definitions
- */
-
- #define GDP_VERSION 1
-
- /*
- * Operations
- */
-
- #define GDP_REPORT 1
- #define GDP_QUERY 2
-
- /*
- * GDP listener port
- */
-
- #define GDP_PORT 1997
-
- /*
- * GDP packet format
- */
-
- struct gdppkt {
- unsigned char version;
- unsigned char code;
- unsigned char count;
- unsigned char reserved;
- struct addrgrp {
- struct in_addr ip;
- unsigned short priority;
- unsigned short holdtime;
- } addrs[1];
- };
-
- #define ALARM_INTERVAL 1
-
- #define TIOCNOTTY _IO('t', 113) /* void tty association */
-
- #define KERNEL "/hp-ux"
- #define MEM "/dev/kmem"
-
- struct nlist nl[] = {
- #define X_PR_VNATAB 0
- { "_pr_vnatab" },
- #define N_RTHOST 1
- { "_rthost" },
- #define N_RTNET 2
- { "_rtnet" },
- #define X_SPACE 3
- { " " },
- { "" },
- };
-
- struct iface {
- char name[IFNAMSIZ];
- short flags;
- struct in_addr ipaddress;
- struct in_addr ipmask;
- struct in_addr ipbroadcast;
- struct iface *next;
- } *iflist;
-
- struct neighbor {
- struct in_addr ip;
- unsigned short priority;
- unsigned short holdtime;
- struct neighbor *next;
- };
-
- struct neighbor *rcvd_neighbors;
- struct neighbor *sent_neighbors;
-
- struct neighbor *default_neighbor;
-
- #define DEFAULT_PRIORITY 100
- #define DEFAULT_HOLDTIME 15
-
- int debug; /* debugging flag */
- int flush; /* flush routing table flag */
- int logging; /* syslog logging flag */
- int hold_time; /* tell others how often to hold */
- int metric; /* how do we run */
- int priority; /* what priority do we advertise */
- int query_time; /* how often to send queries */
- int query;
- int report_time; /* how often to send reports */
- int report;
- int round_robin; /* how often to cycle parallel gws */
- int round;
-
- int m;
- int s;
- struct sockaddr_in from;
- int fromlen;
- struct hostent *hp;
- struct servent *sp;
- unsigned char buf[BUFSIZ];
-
- int timer();
-
- /*
- * main
- * Perform GDP processing depending on what we were told
- */
-
- main (argc, argv)
- int argc;
- char *argv;
- {
- int c, status, on = 1;
- struct gdppkt *gdp;
- struct addrgrp ag;
- struct in_addr bip;
- extern int errno;
- extern char *optarg;
- extern int optind;
-
- debug = 0;
- flush = 0;
- logging = 0;
- metric = 1; /* default to one hop */
- priority = DEFAULT_PRIORITY;
- hold_time = DEFAULT_HOLDTIME;
- query_time = 0;
- report_time = 0;
- round_robin = 0;
- iflist = NULL;
- rcvd_neighbors = NULL;
- sent_neighbors = NULL;
- default_neighbor = NULL;
- bip.s_addr = 0L;
- #ifdef LOG_DAEMON
- openlog("gdpd", LOG_PID, LOG_DAEMON);
- #else
- openlog("gdpd", LOG_PID);
- #endif
- while ((c = getopt(argc, argv, "dfh:lm:p:q:r:t:")) != EOF)
- switch (c) {
- case 'd': /* debug */
- debug = 1;
- break;
- case 'f': /* flush */
- flush = 1;
- break;
- case 'h': /* hold time */
- hold_time = atoi(optarg);
- if (hold_time < 0) {
- syslog(LOG_INFO, "%s: illegal hold time value - %d",
- argv[0], hold_time);
- exit(1);
- }
- break;
- case 'l': /* logging */
- logging = 1;
- break;
- case 'm': /* metric */
- metric = atoi(optarg);
- if (metric < 0) {
- syslog(LOG_INFO, "%s: illegal metric value - %d",
- argv[0], metric);
- exit(1);
- }
- break;
- case 'p': /* priority */
- priority = atoi(optarg);
- if (priority < 0) {
- syslog(LOG_INFO, "%s: illegal priority value - %d",
- argv[0], priority);
- exit(1);
- }
- break;
- case 'q': /* query */
- query_time = atoi(optarg);
- if (query_time < 0) {
- syslog(LOG_INFO, "%s: illegal querying value - %d",
- argv[0], query_time);
- exit(1);
- }
- break;
- case 'r': /* report */
- report_time = atoi(optarg);
- if (report_time < 0) {
- syslog(LOG_INFO, "%s: illegal reporting value - %d",
- argv[0], report_time);
- exit(1);
- }
- break;
- case 't': /* time */
- round_robin = atoi(optarg);
- if (round_robin < 0) {
- syslog(LOG_INFO, "%s: illegal round robin value - %d",
- argv[0], round_robin);
- exit(1);
- }
- round_robin *= 60; /* make seconds */
- break;
- default:
- syslog(LOG_INFO, "%s: illegal switch -%s", argv[0], optarg);
- exit(1);
- }
-
- if (query_time != 0 && report_time != 0) {
- syslog(LOG_INFO, "%s: cannot report and query simultaneously.\n",
- argv[0]);
- exit(1);
- }
- bzero(&ag, sizeof(struct addrgrp));
- while (argc != optind) {
- ag.ip.s_addr = inet_addr(argv[optind]);
- if (ag.ip.s_addr == -1) {
- hp = gethostbyname(argv[optind]);
- if (hp == NULL) {
- syslog(LOG_INFO, "%s: %s: unknown host\n", argv[0],
- argv[optind]);
- exit(1);
- }
- bcopy(hp->h_addr, &ag.ip, sizeof(ag.ip));
- }
- enter_neighbor(&sent_neighbors, &ag);
- optind++;
- }
- query = query_time;
- report = report_time;
- round = round_robin;
- if (!debug) {
- status = fork();
- if (fork() != 0) {
- syslog(LOG_ERR, " %i, fork kills the parent",status);
- exit(0);
- }
- for (c = 0; c < getdtablesize(); c++) {
- (void) close(c);
- }
- (void) open("/", O_RDONLY);
- (void) dup2(0, 1);
- (void) dup2(0, 2);
- c = open("/dev/tty", O_RDWR);
- if (c >= 0) {
- ioctl(c, TIOCNOTTY, (char *)0);
- (void) close(c);
- }
-
- if (metric == 0 || flush) {
- nlist(KERNEL, nl);
- if (nl[X_PR_VNATAB].n_value == 0) {
- syslog(LOG_INFO," failed vnatab nlist ");
- }
- if (nl[N_RTHOST].n_value == 0) {
- syslog(LOG_INFO," failed rthost nlist ");
- exit(1);
- }
- if (nl[N_RTNET].n_value == 0) {
- syslog(LOG_INFO," failed rtnet nlist ");
- exit(1);
- }
- m = open(MEM, O_RDONLY);
- if (m < 0) {
- exit(1);
- }
- }
- if (debug)
- syslog(LOG_DEBUG, "server starting");
-
- #ifdef LOG_DAEMON
- openlog("gdpd", LOG_PID, LOG_DAEMON);
- #else
- openlog("gdpd", LOG_PID);
- #endif
- }
-
- /*
- * Getgetd listener socket
- */
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- syslog(LOG_ERR, "socket: %m");
- exit(1);
- }
-
- #ifdef SO_BROADCAST
- if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
- syslog(LOG_ERR, "setsockopt: %m");
- exit(1);
- }
- #endif
-
- /*
- * Get port we need to pay attention to
- */
- bzero((caddr_t)&from, sizeof(from));
- from.sin_family = AF_INET;
- from.sin_addr.s_addr = INADDR_ANY;
- sp = getservbyname("gdp", "udp");
- if (sp == NULL)
- from.sin_port = htons(GDP_PORT);
- else
- from.sin_port = htons(sp->s_port);
-
- if (bind(s, &from, sizeof(from)) < 0) {
- syslog(LOG_ERR, "bind: %m");;
- exit(1);
- }
-
- build_iflist();
- send_query(bip);
- signal(SIGALRM, timer);
- timer();
-
- /*
- * Now do the work
- */
-
- for (;;) {
- syslog(LOG_ERR, "in for loop");
- fromlen = sizeof(from);
- c = recvfrom(s, buf, sizeof(buf), 0, (caddr_t)&from, &fromlen);
- if (c <= 0) {
- if (errno == EINTR)
- continue;
- syslog(LOG_ERR, "recvfrom: %m");
- exit(1);
- }
- gdp = (struct gdppkt *)buf;
- if (gdp->version != GDP_VERSION)
- continue;
- if (debug) {
- hp = gethostbyaddr(&from.sin_addr, sizeof(struct in_addr),
- AF_INET);
- syslog(LOG_DEBUG, "%s received from %s", (gdp->code == GDP_REPORT
- ? "REPORT" : (gdp->code == GDP_QUERY ? "QUERY" : "UNKNOWN")),
- hp ? hp->h_name : (char *)inet_ntoa(from.sin_addr));
- }
- switch (gdp->code) {
- case GDP_REPORT:
- if (report_time == 0)
- process_report(&from, gdp);
- break;
- case GDP_QUERY:
- if (report_time != 0)
- process_query(&from, gdp);
- break;
- default:
- break;
- }
- }
- }
-
- /*
- * find_neighbor
- * Look for a given neighbor in the list and return ptr or NULL
- */
-
- struct neighbor *find_neighbor (nt, ip)
- struct neighbor **nt;
- struct in_addr ip;
- {
- struct neighbor *p;
-
- for (p = *nt; p != NULL; p = p->next)
- if (p->ip.s_addr == ip.s_addr)
- break;
- return(p);
- }
-
- /*
- * enter_neighbor
- * Enter a new neighbor into the table in priority order
- */
-
- enter_neighbor (nt, ag)
- struct neighbor **nt;
- struct addrgrp *ag;
- {
- struct neighbor *p, *pp, *newp;
-
- newp = (struct neighbor *)malloc(sizeof(struct neighbor));
- if (newp == NULL)
- return;
-
- newp->ip = ag->ip;
- newp->priority = ag->priority;
- newp->holdtime = ag->holdtime;
- newp->next = NULL;
- for (p = pp = *nt; p != NULL; pp = p, p = p->next)
- if (p->priority >= newp->priority)
- continue;
- if (p == NULL) {
- if (*nt == NULL)
- *nt = newp;
- else
- pp->next = newp;
- } else if (p == pp) {
- newp->next = *nt;
- *nt = newp;
- } else {
- pp->next = newp;
- newp->next = p;
- }
- }
-
- /*
- * delete_neighbor
- * Remove neighbor from the list
- */
-
- delete_neighbor (nt, dp)
- struct neighbor **nt;
- struct neighbor *dp;
- {
- struct neighbor *p, *pp;
-
- for (p = pp = *nt; p != NULL; pp = p, p = p->next)
- if (p == dp)
- break;
- if (p == pp)
- *nt = p->next;
- else
- pp->next = p->next;
- free(p);
-
- }
-
- /*
- * robin_neighbor
- * Perform round robin on received neighbors of equal priority
- */
-
- robin_neighbor ()
- {
- struct neighbor *p, *pp;
-
- /*
- * Find where next lower priorty starts
- */
- for (p = pp = rcvd_neighbors; p != NULL; p = p->next)
- if (default_neighbor->priority > p->priority)
- break;
- /*
- * If there is only a single entry, then don't bother
- */
- if (default_neighbor == pp)
- return;
-
- /*
- * Splice old one at end of equal priority list
- */
- rcvd_neighbors = default_neighbor->next;
- pp->next = default_neighbor;
- default_neighbor->next = p;
- update_routes();
- }
-
- /*
- * process_report
- * handle processing of GDP report messages
- */
-
- process_report (from, gdp)
- struct sockaddr_in *from;
- struct gdppkt *gdp;
- {
- struct neighbor *p;
- struct addrgrp *a;
-
- a = gdp->addrs;
- while (gdp->count-- > 0) {
- if (valid_address(a->ip)) {
- p = find_neighbor(&sent_neighbors, a->ip);
- if (p != NULL)
- p->holdtime = a->holdtime;
- p = find_neighbor(&rcvd_neighbors, a->ip);
- if (p == NULL || p->priority != a->priority) {
- if (p != NULL) {
- if (flush) {
- flush_routes(p->ip);
- }
- delete_neighbor(&rcvd_neighbors, p);
- }
- enter_neighbor(&rcvd_neighbors, a);
- update_routes();
- round = round_robin; /* restart timer */
- } else
- p->holdtime = a->holdtime;
- } else if (debug)
- syslog(LOG_DEBUG, "invalid IP address: %s",
- inet_ntoa(a->ip));
- a++;
- }
- }
-
- /*
- * process_query
- * Process a QUERY and return a REPORT
- */
-
- process_query (from, gdp)
- struct sockaddr_in *from;
- struct gdppkt *gdp;
- {
- }
-
- /*
- * timer
- * Wake up once in a while and process running timers, etc.
- */
-
- timer ()
- {
- struct neighbor *p, *dp;
- struct in_addr bip;
-
- bip.s_addr = 0L;
- if (report_time != 0) {
- if (report <= 0) {
- send_report(bip);
- report = report_time;
- } else
- report -= ALARM_INTERVAL;
- } else {
- if (query_time != 0) {
- query -= ALARM_INTERVAL;
- if (query <= 0) {
- for (p = sent_neighbors; p != NULL; p = p->next)
- send_query(p->ip);
- query = query_time;
- }
- }
- for (p = rcvd_neighbors; p != NULL; ) {
- dp = p;
- p = p->next;
- dp->holdtime -= ALARM_INTERVAL;
- if (dp->holdtime <= 0) {
- if (flush) {
- flush_routes(dp->ip);
- }
- delete_neighbor(&rcvd_neighbors, dp);
- update_routes();
- round = round_robin; /* restart timer */
- }
- }
- }
- if (round_robin != 0) {
- round -= ALARM_INTERVAL;
- if (round <= 0) {
- if (default_neighbor != NULL)
- robin_neighbor();
- round = round_robin;
- }
- }
- alarm(ALARM_INTERVAL);
- }
-
- /*
- * send_query
- * Send a GDP query message
- */
-
- send_query (ip)
- struct in_addr ip;
- {
- struct gdppkt gdp;
- struct sockaddr_in sin;
- struct iface *ifp;
- struct neighbor *p;
-
- bzero(&gdp, sizeof(gdp));
- gdp.version = GDP_VERSION;
- gdp.code = GDP_QUERY;
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr = ip;
- if (sp == NULL)
- sin.sin_port = htons(GDP_PORT);
- else
- sin.sin_port = htons(sp->s_port);
- if (ip.s_addr == 0L) {
- for (ifp = iflist; ifp != NULL; ifp = ifp->next) {
- if ((ifp->flags & IFF_BROADCAST) == 0)
- continue;
- sin.sin_addr = ifp->ipbroadcast;
- (void) sendto(s, &gdp, sizeof(gdp) - sizeof(struct addrgrp), 0,
- &sin, sizeof(sin));
- }
- for (p = sent_neighbors; p != NULL; p = p->next)
- send_query(p->ip);
- } else
- (void) sendto(s, &gdp, sizeof(gdp) - sizeof(struct addrgrp), 0,
- &sin, sizeof(sin));
- }
-
- /*
- * send_report
- * Send a GDP REPORT message
- */
-
- send_report (ip)
- struct in_addr ip;
- {
- }
-
- /*
- * build_iflist
- * Build a list of interfaces and accompaning information
- */
-
- build_iflist ()
- {
- int n;
- char ifbuf[BUFSIZ];
- struct ifconf ifc;
- struct ifreq *ifr, ifreq;
- struct iface *ifp;
- struct sockaddr_in *sa;
- short flags;
-
- ifc.ifc_len = sizeof(ifbuf);
- ifc.ifc_buf = ifbuf;
- if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
- syslog(LOG_ERR, "ioctl(IFCONF): %m");
- exit(1);
- }
- ifr = ifc.ifc_req;
- for (n = ifc.ifc_len / sizeof(struct ifreq); n > 0; n--, ifr++) {
- /*
- * Ignore the loopback interface
- */
- if (strncmp(ifr->ifr_name, "lo", 2) == 0)
- continue;
- /*
- * Ignore all interfaces which aren't running
- */
- strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
- if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
- continue;
- else
- flags = ifreq.ifr_flags;
- if ((flags & IFF_RUNNING) == 0)
- continue;
- sa = (struct sockaddr_in *)&ifr->ifr_addr;
- /*
- * Ignore 0.0.0.0
- */
- if (sa->sin_addr.s_addr == 0L)
- continue;
- ifp = (struct iface *)malloc(sizeof(struct iface));
- if (ifp == NULL)
- break;
- bzero(ifp, sizeof(struct iface));
- strncpy(ifp->name, ifr->ifr_name, sizeof(ifp->name));
- ifp->ipaddress = sa->sin_addr;
- ifp->flags = flags;
- strncpy(ifreq.ifr_name, ifp->name, sizeof(ifp->name));
- if (ioctl(s, SIOCGIFNETMASK, &ifreq) >= 0) {
- sa = (struct sockaddr_in *)&ifreq.ifr_addr;
- ifp->ipmask = sa->sin_addr;
- }
- if (ifp->flags & IFF_BROADCAST) {
- strncpy(ifreq.ifr_name, ifp->name, sizeof(ifp->name));
- if (ioctl(s, SIOCGIFBRDADDR, &ifreq) >= 0) {
- sa = (struct sockaddr_in *)&ifreq.ifr_addr;
- ifp->ipbroadcast = sa->sin_addr;
- }
- }
- if (iflist == NULL)
- iflist = ifp;
- else {
- ifp->next = iflist;
- iflist = ifp;
- }
- }
- }
-
- /*
- * valid_address
- * Determine if give IP address is something we grok.
- */
-
- valid_address (ip)
- struct in_addr ip;
- {
- struct iface *ifp;
-
- for (ifp = iflist; ifp != NULL; ifp = ifp->next) {
- if ((ip.s_addr & ifp->ipmask.s_addr) ==
- (ifp->ipaddress.s_addr & ifp->ipmask.s_addr))
- return(1);
- }
- return(0);
- }
-
- /*
- * update_routes
- * Update default route if necessary when something changes
- */
-
- update_routes ()
- {
- struct rtentry rt;
- struct sockaddr_in *sa;
- struct in_addr inet_makeaddr();
-
- if (default_neighbor == rcvd_neighbors)
- return;
-
- bzero(&rt, sizeof(rt));
- rt.rt_flags = RTF_UP;
- if (metric > 0)
- rt.rt_flags |= RTF_GATEWAY;
- sa = (struct sockaddr_in *)&rt.rt_dst;
- sa->sin_family = AF_INET;
- sa->sin_addr = inet_makeaddr(0, INADDR_ANY);
- sa = (struct sockaddr_in *)&rt.rt_gateway;
- sa->sin_family = AF_INET;
- if (default_neighbor != NULL) {
- sa->sin_addr = default_neighbor->ip;
- if (metric == 0)
- flush_vna();
- if (logging)
- syslog(LOG_INFO, "deleting old default: %s",
- inet_ntoa(sa->sin_addr));
- if (!debug)
- (void) ioctl(s, SIOCDELRT, &rt);
- }
- default_neighbor = rcvd_neighbors;
- if (default_neighbor == NULL)
- return;
- sa->sin_addr = default_neighbor->ip;
- if (logging)
- syslog(LOG_INFO, "adding new default: %s",
- inet_ntoa(sa->sin_addr));
- if (!debug)
- (void) ioctl(s, SIOCADDRT, &rt);
- }
-
-
- /*
- * flush_routes
- * Flush the routing table. We do this when asked.
- */
-
- flush_routes (ip)
- struct in_addr ip;
- {
- struct in_addr in;
- struct mbuf mb;
- register struct rtentry *rt;
- register struct mbuf *mbp;
- struct mbuf **routehash;
- int rthashsize, i, doinghost = 1;
-
- if (logging)
- syslog(LOG_INFO, "flushing routes via %s", inet_ntoa(ip));
- lseek(m, nl[X_SPACE].n_value, 0);
- read(m, &rthashsize, sizeof (rthashsize));
- routehash = (struct mbuf **)malloc(rthashsize*sizeof (struct mbuf *));
- lseek(m, nl[N_RTHOST].n_value, 0);
- read(m, routehash, rthashsize*sizeof (struct mbuf *));
- again:
- for (i = 0; i < rthashsize; i++) {
- syslog(LOG_INFO, "entered again ");
- if (routehash[i] == 0)
- continue;
- syslog(LOG_INFO, "passed again ");
- mbp = routehash[i];
- while (mbp) {
- lseek(m, mbp, 0);
- read(m, &mb, sizeof (mb));
- rt = mtod(&mb, struct rtentry *);
- if ((rt->rt_flags & RTF_GATEWAY) &&
- rt->rt_gateway.sa_family == AF_INET) {
- in = ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr;
- if (in.s_addr == ip.s_addr) {
- if (logging) {
- in = ((struct sockaddr_in *)&rt->rt_dst)->sin_addr
- ;
- syslog(LOG_INFO, "flushing route %s",
- inet_ntoa(in));
- }
- if (!debug)
- (void) ioctl(s, SIOCDELRT, (caddr_t)rt);
- }
- }
- mbp = mb.m_next;
- }
- }
- if (doinghost) {
- lseek(m, nl[N_RTNET].n_value, 0);
- read(m, routehash, rthashsize*sizeof (struct mbuf *));
- doinghost = 0;
- goto again;
- }
- free(routehash);
- }
-
- flush_vna()
-
- {
-
- int n, status;
-
- extern int errno;
-
- struct vnatab pr_vnatab[VNATAB_SIZE];
-
- struct vnatab vna_entry;
-
- syslog(LOG_ERR,"flushing vnatab");
-
- n = open(MEM,O_RDWR);
-
- if (n == -1) {
- syslog(LOG_ERR,"%i, mem open failed",errno);
- exit(1);
- }
-
- (void) memset(pr_vnatab, 0, sizeof(pr_vnatab));
-
- status = (lseek(n, (long) nl[X_PR_VNATAB].n_value, 0));
- if (status == -1) {
- syslog(LOG_ERR,"%i, lseek failed",errno);
- exit(1);
- }
-
- (void) write(n, pr_vnatab, sizeof(pr_vnatab));
- (void) close(n);
-
- }
-